package com.work.aop;

import com.work.annotation.Transactional;
import com.work.ioc.ApplicationContext;
import com.work.utils.TransactionManager;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.SQLException;
import java.util.*;

/**
 * @program: spring-work
 * @description
 * @author: luffy
 * @create: 2020-07-10 09:32
 */
public class ProxyFactory {
    private String packageName = "com/work";
    private Set<Class<?>> classSet = new HashSet<>();
    private TransactionManager transactionManager = TransactionManager.getTransactionManager();
    private ApplicationContext applicationContext;

    public ProxyFactory() {
        applicationContext = new ApplicationContext();
        findClass();
        try {
            Map<String, Object> map = doProxy();
            for (String proxyName : map.keySet()) {
                Map<String, Object> ioc = applicationContext.getIoc();
                if (ioc.containsKey(proxyName)) {
                    ioc.put(proxyName, map.get(proxyName));
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    private void findClass() {
        Set<Class<?>> acClassSet = ApplicationContext.getClassSet();
        for (Class<?> aClass : acClassSet) {
            Method[] declaredMethods = aClass.getDeclaredMethods();
            for (Method declaredMethod : declaredMethods) {
                if (declaredMethod.isAnnotationPresent(Transactional.class)) {
                    classSet.add(aClass);
                }
            }
        }

    }


    public Map<String,Object> doProxy() throws Exception{
        Map<String, Object> proxyMap = new HashMap<>();
        for (Class<?> aClass : classSet) {
            Object proxy = null;
            if (aClass.getInterfaces()!=null) {
                Object obj = applicationContext.getBean(aClass.getSimpleName());
                proxy = Proxy.newProxyInstance(this.getClass().getClassLoader(), aClass.getInterfaces(), new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        Object invoke;
                        try {
                            transactionManager.beginTransaction();
                             invoke = method.invoke(obj, args);
                            transactionManager.commit();
                        } catch (SQLException e) {
                            e.printStackTrace();
                            transactionManager.rollback();
                            throw e;
                        }
                        return invoke;
                    }
                });
            }else {
                Object obj = applicationContext.getBean(aClass.getSimpleName());
                proxy = Enhancer.create(this.getClass(), new MethodInterceptor() {
                    @Override
                    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                        Object invoke;
                        try {
                            transactionManager.beginTransaction();
                            invoke = method.invoke(obj, objects);
                            transactionManager.commit();
                        } catch (SQLException e) {
                            e.printStackTrace();
                            transactionManager.rollback();
                            throw e;
                        }
                        return invoke;
                    }
                });
            }


            proxyMap.put(aClass.getSimpleName(),proxy);
        }
        return proxyMap;
    }
    public ApplicationContext getApplicationContext() {
        return applicationContext;
    }
}
